<?php
 
  
 define('DLS_DONTCREATE' , "DataLatte class is not meant to be instantiated. All methods are to be used statically.");
 define('DLS_CANTSELECTDB', "Can't select database %s");
 define('DLS_CANTCONNECT', "Can't connect to server %s (user %s)");
 define('DLS_QUERYERROR',  "Error when executing a query: <pre>%s</pre><div class=mysqlError>%s</div>");
 define('DLS_NOQUERY', "Query not shown");
 
 /**
  * Do not modify from here
  */
 interface iDataConnection{

	public function getreader($query);
			
	public function getsingle($query);
	
	public function update($query);
	
	public function close();

 }
 
 interface iRecordGlue{
	
	public function getstickers($arr);
	
 }
 
 interface iRenderable{
 }
 
 class DataReader{
	 
	 public $result = 0;
	 
	 function __construct($result){
		 $this->result = $result;
	 }
	 
	 function read($mode = MYSQL_BOTH){
		return mysql_fetch_array($this->result, $mode);
	 }
	 
	 function getrows(){
		 return mysql_num_rows($this->result);
	 }
	 
	 function getcolumns(){
		 return mysql_num_fields($this->result);
	 }
 }
 
  class DataConnection implements iDataConnection{

	public $connection = 0;
	
	function __construct($parameters){

		$this->connection = mysql_connect(
					$parameters->host,
					$parameters->user,
					$parameters->pass);
		
		if(!$this->connection){
			throw new Exception(sprintf(DLS_CANTCONNECT, $parameters->host, $parameters->user));
		}
		
		if(!mysql_select_db($parameters->db)){
			throw new Exception(sprintf(DLS_CANTSELECTDB, $parameters->db));
		}
		
	}
	
	function __destruct(){
		$this->close();
	}
	
	private function queryornot($query){
		return ConnectionParameters::$debug ? $query : DLS_NOQUERY;
	}
	
	public function getreader($query){
		
		$result = mysql_query($query, $this->connection);
		
		if(!$result)
			throw new Exception(sprintf(DLS_QUERYERROR, mysql_error(), $this->queryornot($query)));
		
		return new DataReader($result);
	}
	
		
	public function getsingle($query){
		
		$result = mysql_query($query, $this->connection);
		
		if(!$result)
			throw new Exception(sprintf($GLOBALS[DLS_QUERYERROR], mysql_error(), $this->queryornot($query)));
		
		$row = mysql_fetch_row($result);
		
		return $row[0];
	}
	
	public function update($query){
		
		$result = mysql_query($query, $this->connection);
		
		if(!$result)
			throw new Exception(sprintf(DLS_QUERYERROR, mysql_error(),$this->queryornot($query)));
		
		return mysql_affected_rows($this->connection);
	}
	
	public function close(){
		mysql_close($this->connection);
	}
	
 }

 class DataLatte implements iDataConnection{
	 
	public static $current = NULL;
	
	function __construct(){
		throw new Exception($GLOBALS[DLS_DONTCREATE]);
	}
	
	public static function init(){
		if(self::$current === NULL)
			self::$current = new DataConnection(new ConnectionParameters());
	}
	
	public function getcache($query){
		
		$a = array();
		$reader = self::getreader($query);
		
		while($r = $reader->read())
			$a[] = $r;
		
		return $a;
	}
	
	public function getreader($query){
		self::init();
		return self::$current->getreader($query);
	}
		
	public function getsingle($query){
		self::init();
		return self::$current->getsingle($query);
	}
	
	public function update($query){
		self::init();
		return self::$current->update($query);
	}
	
	public function close(){
		init();
		return self::$current->close();
	}
	
	public static function recognize($array, $record, $deep = TRUE){
		
		foreach($array as $key => $value){
			
			$value = stripslashes($value);
			$key = str_replace("__", ".", $key);
			$pos = strpos($key, ".");
			$prop = "";
			
			if($pos === FALSE){
				$prop = $key;
			}elseif(substr($key, 0, $pos) != $record->gettable()){
				continue;
			}else{
				$prop = substr($key, (strlen($key) - $pos - 1) * -1);
			}
			
			if(property_exists($record, $prop)){
				$record->{$prop} = $value;
			}
		}
		
		if($deep){
			if($record instanceof iRecordGlue){
				$stickers = $record->getstickers($array);
				if(is_array($stickers) && sizeof($stickers)){
					foreach($stickers as $key => $sticker){
						$propname =  $key;
						if(is_numeric($key)){
							$foo = new $sticker();
							$propname = $foo->gettable();
						}
						if(property_exists($record, $propname)){
							$obj = $record->{$propname};
						}else{
							$obj = new $sticker();
						}						
						self::recognize($array, $obj, $deep);
						
						$record->{$propname} = $obj;
					}
				}
			}
		}
	}
	
	
	
	public static function oneof($classname, $query){
		$obj = new $classname();
		$arr = self::getcache($query);
		
		if(sizeof($arr)){
			self::recognize($arr[0], $obj);
		}else{
			return FALSE;
		}
		
		return $obj;
	}
	
	public static function arrayof($class, $query){
		$arr = self::getcache($query);
		$result = array();
		
		foreach($arr as $row){
			$obj = new $class();
			self::recognize($row, $obj);
			$result[] = $obj;
		}
		
		return $result;
	}
	
	public static function dumprecords(){
			function dumptable($table){
			
				$fields = array();
				$primkey = array();
				$keys = array();
				$justfields = array();
				$reader = DataLatte::getreader("describe $table");
				
				while($r = $reader->read()){
					$fields[] = $r[0];
					
					if($r['Extra'] == "auto_increment"){
						$primkey[] = $r[0];
					}elseif($r['Key'] == "PRI"){
						$keys[] = $r[0];
					}else{
						$justfields[] = $r[0];
				}
			}
			
			$checks = array();
			
			foreach(array_merge($keys, $primkey) as $value){
				$checks[] = sprintf('isset($this->%s)', $value);
			}
			
			$selects = array();
			
			foreach($fields as $value){
				$selects[] = sprintf(' "$t.%s AS \'$t.%s\'" ', $value, $value);
			}
			
			$code = sprintf('
			
			class __TABLE__Base extends DataRecord{
			
				public $%s;
				public static function all($table = false){ if($table) $t = "__TABLE__"; else $t = ""; return array( %s ); }
				public function gettable(){ return "__TABLE__"; }
				public function getautokey(){ return array( %s ); }
				public function getkeys(){ return array( %s ); }
				public function getfields(){ return array( %s ); }
				public function isinserted(){ return %s; }
			
			} ',
				implode(', $', $fields),
				implode(', ', $selects),
				implode(", ", makepairs($primkey)),
				implode(", ", makepairs($keys)),
				implode(", ", makepairs($justfields)),
				sizeof($checks) ? implode(" && ", $checks) : 1
			);
			
			$code = str_replace("__TABLE__", $table, $code);
			
			echo "&lt;" . "?php " . $code . " ?" . "&gt;";
			
		}
			
		function makepairs($arr){
			
			$r = array();
			
			foreach($arr as $value){
				$r[] = sprintf('"%s" => $this->%s', $value, $value);
			}
			
			return $r;
			
		}
		
		$reader = DataLatte::getreader("SHOW TABLES");
		$tables = array();
		
		while($r = $reader->read()) $tables[] = $r[0];
		
		foreach($tables as $table) dumptable($table);
	}
	
	public static function escape($array){
		self::init();
		if(is_array($array)){
			foreach($array as $key => $value){
				$array[$key] = DataLatte::escape($value);
			}
			return $array;
		}else{
			return mysql_real_escape_string($array);
		}
		
	}
	
 }
 
 abstract class DataRecord{
	 
	 abstract public function gettable();
	 
	 abstract public function getautokey();
	 
	 abstract public function getkeys();
	 
	 abstract public function getfields();
	 
	 abstract public function isinserted();
	 
	 public function getall(){
		 return array_merge(
				$this->getautokey(),
				$this->getkeys(),
				$this->getfields());
	 }
	 
	 public function getallkeys(){
		 return array_merge(
				$this->getautokey(),
				$this->getkeys());
	 }
	 
	 public function insertquery(){
		 
		 $all = array_merge($this->getkeys(), $this->getfields());
		 
		 $all = DataLatte::escape($all);
		 
		 return str_replace("'NULL'", "NULL", sprintf(
			"INSERT INTO %s(`%s`) VALUES('%s')",
			$this->gettable(),
			implode("`, `", array_keys($all)),
			implode("', '", array_values($all))
			));
		 
	 }
	 
	 public function updatequery($field = ""){
		 
		 if(strlen($field) == 0){
			 return  sprintf(
				"UPDATE %s SET %s WHERE %s",
				$this->gettable(),
				implode(", ", $this->equalize(array_merge($this->getfields(), ((sizeof($this->getautokey()) ? $this->getkeys() : array())) ))),
				$this->createwhere( (sizeof($this->getautokey()) ? $this->getautokey() : $this->getallkeys()) )
				);
		 }else{
			  return  sprintf(
				"UPDATE %s SET %s = '%s' WHERE %s",
				$this->gettable(),
				$field,
				$this->${field},
				$this->createwhere( (sizeof($this->getautokey()) ? $this->getautokey() : $this->getallkeys()) )
				);
		 }
		 
	 }
	 
	 public function deletequery(){
		 return sprintf(
				"DELETE FROM %s WHERE %s",
				$this->gettable(),
				$this->createwhere($this->getallkeys())
				);
	 }
	 
	 public function insert($connection = 0){
		 
		 $arr = $this->getautokey();
		 $ak = 0;
		 
		 if(sizeof($arr)){
			 $keys = array_keys($arr);
			 $ak = $keys[0];
		 }
		 
		 if($connection){
			 $connection->update($this->insertquery());
			 
			 if($ak){
				 $this->{$ak} = $connection->getsingle("SELECT LAST_INSERT_ID()");
			 }
			 
		 }else{
			 DataLatte::update($this->insertquery());
			 
			 if($ak){
				 $this->{$ak} = DataLatte::getsingle("SELECT LAST_INSERT_ID()");
			 }
		 }
	 }
	 
	 public function update($connection = 0){
		 if($connection){
			 $connection->update($this->updatequery());
		 }else{
			 DataLatte::update($this->updatequery());
		 }
	 }
	 
	 public function delete($connection = 0){
		 
		 if($connection){
			 $connection->update($this->deletequery());
		 }else{
			 DataLatte::update($this->deletequery());
		 }
	 }
	 
	 public function save($connection = 0){
		 
		 if(isinserted()){
			 update($this->$connection);
		 }else{
			 insert($this->$connection);
		 }
	 }
	 
	 private function equalize($arr){
		 
		 $r = array();
		 $arr = DataLatte::escape($arr);
		
		foreach($arr as $key=>$value){
			if($value == "NULL"){
				$r[] = sprintf(" `%s` = %s", $key, $value);
			}else{
				$r[] = sprintf(" `%s` = '%s'", $key, $value);
			}
		}
		
		return $r;
	 }
	 
	 private function createwhere($arr){
		
		return implode(" AND ", $this->equalize($arr));
		
	 }

 }
	

  

?>